<?php
class Model{
	private static $ins=null;
	private $cfg=array();
	private $link=null;
	private $stmt=null;
	private $error_code=0;
	private $error_info='';

	public $fields=null;
	public $primaryKey=null;
	public $dbprefix=null;
	public $table=null;
	public $full_table=null;
	public $last_sql=null;

	public function __construct($table='',$pk='',$pre=''){
        if(!class_exists('PDO')) exit('PDO未开启'); 
        $this->cfg=VG::$config;
		$this->connect();
		$this->dbprefix=$pre?$pre:VG::$db['dbprefix'];
		$class=get_called_class();
		if(!$table) $table=substr($class, 0,strpos($class,'Model'));
		
		if($table) {
			$this->table=$table;
			$this->full_table=$this->dbprefix.$table;
			$this->fields=$this->setTable($this->full_table);
		}
        if($pk) {
        	$this->primaryKey=$pk;
        }else{
        	if($this->table){
        		$a=$this->setTableAddon($this->full_table);
				$this->primaryKey=$a['pk'];	
        	}

        }
    }
	public function __destruct(){
		$this->close();
	}
	protected function connect(){
		static $link=null;
		if($link){
			$this->link=$link;
		}else{
			$config_db=VG::$db;
			if(!isset($config_db['dsn'])) exit('db need dsn param!');
			try{
				$link=$this->link=new PDO($config_db['dsn'],$config_db['username'],$config_db['password']);
				$this->link->setAttribute(PDO::ATTR_ERRMODE,  PDO::ERRMODE_EXCEPTION);
				$this->link->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,  true);
				$this->link->exec("set names {$config_db['char_set']}");
				$this->link->exec("SET sql_mode=''");
			}catch(PDOException $e){
				echo "数据库连接失败：".$e->getMessage();
				exit;
			}
		}
	}
	public function getLink(){
		return $this->link;
	}
	public function last_sql(){
		return $this->last_sql;
	}
	protected function replace_current_tablename($sql){
		return preg_replace('/__TB__/ms',$this->full_table,$sql);
	}
	protected function replace_tablename($sql){
		return preg_replace('/__(.*?)__/ims',$this->dbprefix.'$1',$sql);
	}
	public function exec($query,$data=array()){
		try{
			$query=$this->replace_current_tablename($query);
			$query=$this->replace_tablename($query);
			$r=$this->link->exec($query);
			$this->rec_sql($query);
			$this->last_sql=$query;
		}catch(PDOException $e){
			if($msg=$this->err(0)){
				$this->_log_err($msg.$query.'\n');
			}
			exit($query.'||||'.$e->getMessage());
		}	
		return $r;
	}
	protected function parse_sql($sql,$data=array()){
		try{
			$stmt=$this->link->prepare($sql);
			$this->stmt=$stmt;
			$r=$stmt->execute($data);
		}catch(PDOException $e){
			if($msg=$this->err(1)){
				$this->_log_err($msg.$sql.serialize($data).'\n');
			}
			exit($sql.'||||'.$e->getMessage());
		}	
		return $r;
	}
	//需要prepare 需要绑定数据 有stmt
	public function query($query,$data=array()){
		$query=$this->replace_current_tablename($query);
		$query=$this->replace_tablename($query);
		$r=$this->parse_sql($query,$data);
		$this->rec_sql($query);
		$this->last_sql=$query;
		return $r;
	}
	public function err(){
		$is_statement=func_get_arg(0);
		if($is_statement){
			$this->error_code=$this->stmt->errorCode();
			$this->error_info=implode(' ',$this->stmt->errorInfo());
		}else{
			$this->error_code=$this->link->errorCode();
			$this->error_info=implode(' ',$this->link->errorInfo());
		}
		if($this->error_code>0){
			return $this->error_code.':'.$this->error_info;
		}else{
			return false;
		}
	}
	//
	public function fetch_array($result){
		return $this->stmt->fetch(PDO::FETCH_ASSOC);
	}
	public function fetch_array_all($result){
		return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
	}
	public function fetch_row($result){
		return $this->stmt->fetch(PDO::FETCH_NUM);
	}
	public function fetch_assoc($result){
		return $this->stmt->fetch(PDO::FETCH_ASSOC);
	}
	public function affected_rows(){
		return $this->stmt->rowCount();
	}
    public function get_error($return_str=1,$com='<br />'){
        if($return_str==1){
			return implode($com,$this->error);
		}else{
			return $this->error;
		}
    }
	public function free_result($result) {
		return true;
	}
	public function insert_id() {
		return $this->link->lastInsertId();
	}
	public function escape($d){
		if(!GPC){
			return addslashes_array($d);
		}else{
			return $d;
		}
	}
	public function close(){
		return true;
	}
	protected function _log_err($d){
		if(!is_dir('./runtime')) {
			@mkdir('./runtime',0777,true);
		}
		$d.="\n";
		return file_put_contents('./runtime/error_mysql_log.txt',$d,FILE_APPEND);
	}
	protected function rec_sql($sql){
		if(defined('DEBUG') and DEBUG==TRUE and VG::$project_debug) debug::addmsg($sql,2);
	}
	protected function cache($method,$sql,$data,$cachetime){
		$sql=$this->replace_current_tablename($sql);
		$sql=$this->replace_tablename($sql);
		$name=md5(json_encode(array($sql,$data)));
		$a=substr($name,0,1);
		$b=substr($name,1,1);
		$cachefile='./runtime/sql_cache/'.$a.'/'.$b.'/'.$name.'.php';
		$dir=dirname($cachefile);
		if(!is_dir($dir)){
			@mkdir($dir,0777,true);
		}
        if(!file_exists($cachefile) || filemtime($cachefile)+$cachetime<time()){
            $DATA=$this->$method($sql,$data,-1);
            array_save($DATA,$cachefile);
        }else{
            $DATA=include($cachefile);
        }
        return $DATA;
	}
	//insert into __member__ %s
	public function insert($sql,$data=array()){
		$sql=$this->replace_current_tablename($sql);
		$sql=$this->replace_tablename($sql);
		$q=$this->ready($sql,$data);
		$result=$this->query($q['sql'],$q['data']);
		if($result){
			return $this->insert_id();
		}
		return false;
	}
	//update __member__ set %s where itemid=:itemid
	public function update($sql,$data=array(),$other_data=array()){
		$sql=$this->replace_current_tablename($sql);
		$sql=$this->replace_tablename($sql);
		$q=$this->ready($sql,$data);
		return $this->query($q['sql'],array_merge($q['data'],$other_data));
	}
	public function delete($sql,$data=array()){
		return $this->query($sql,$data);
	}
	public function select($sql,$data=array(),$cachetime=-1){
		if($cachetime>0) return $this->cache(__FUNCTION__,$sql,$data,$cachetime);
		$result=$this->query($sql,$data);
		if($result){
			$r=$this->fetch_array_all($result);
			$this->free_result($result);
			return $r;
		}else{
			return false;
		}
	}
	public function find($sql,$data=array(),$cachetime=-1){
		if($cachetime>0) return $this->cache(__FUNCTION__,$sql,$data,$cachetime);
		$result=$this->query($sql,$data);
		if($result){
			$r=$this->fetch_array($result);
			$this->free_result($result);
			return $r;
		}else{
			return false;
		}
	}
	public function get_field($sql,$data=array(),$cachetime=-1){
		if($cachetime>0) return $this->cache(__FUNCTION__,$sql,$data,$cachetime);
		$result=$this->query($sql,$data);
		if($result){
			$info=$this->fetch_row($result);
			$this->free_result($result);
			if(!empty($info)){
				return $info[0];
			}else{
				return '';
			}
		}else{
			return '';
		}
	}
	public function get_column($sql,$data=array(),$cachetime=-1){
		if($cachetime>0) return $this->cache(__FUNCTION__,$sql,$data,$cachetime);
		$d=array();
        $result=$this->query($sql,$data);
		if($result){
			while($r=$this->fetch_row($result)){
                $d[]=$r[0];
            }
			$this->free_result($result);
		}
        return $d;
	}
	public function i($data){
		return $this->insert("insert into __{$this->table}__ %s",$data);
	}
	public function u($itemid,$data){
		$itemid=intval($itemid);
		return $this->update("update __{$this->table}__ set %s where {$this->primaryKey}='{$itemid}'",$data);
	}
	public function d($itemid){
		$itemid=intval($itemid);
		return $this->delete("delete from __{$this->table}__ where {$this->primaryKey}='{$itemid}'");
	}
	public function s($order='',$limit='',$cachetime=-1){
		if($order){
			$o="order by {$order}";
		}else{
			$o="order by {$this->primaryKey} asc";
		}
		if($limit){
			$l="limit {$limit}";
		}else{
			$l="";
		}
		return $this->select("select * from __{$this->table}__ {$o} {$l}",array(),$cachetime);
	}
	public function f($itemid,$cachetime=-1){
		$itemid=intval($itemid);
		return $this->find("select * from __{$this->table}__ where {$this->primaryKey}='{$itemid}'",array(),$cachetime);
	}
	public function gf($itemid,$field,$cachetime=-1){
		$itemid=intval($itemid);
		return $this->get_field("select {$field} from __{$this->table}__ where {$this->primaryKey}='{$itemid}'",array(),$cachetime);
	}
	public function gc($field,$cachetime=-1){
		return $this->get_column("select {$field} from __{$this->table}__ ",array(),$cachetime);
	}

	public function ready($query,$array=null,$special=array()){
		$arr=array();
		if(is_null($array)) $array=$_POST;
		$method=strtolower(substr($query,0,6));
		if($method=='insert'){
			$tablename=preg_replace("/insert into(?:\s)*(?:`)?([a-z0-9_-]+)(?:`)?(?:\s)*.*/is","\\1",$query);
		}else if($method=='update'){
			$tablename=preg_replace("/update(?:\s)*(?:`)?([a-z0-9_-]+)(?:`)?(?:\s)*.*/is","\\1",$query);
		}else{
			exit('方法名只能为：insert 或 update');
		}
		$fieldList=$this->setTable($tablename);
		$arr=$this->remove_arr($fieldList,$array,$special);
		
		$data=$arr[1];
		//$data=$this->escape($data);//new add
		if($method=='insert'){
			$queryvalues='(`'.implode('`,`', array_keys($arr[0]))."`) values (".implode(",", array_values($arr[0])). ")";
		}else if($method=='update'){
			$queryvalues='';
			foreach ($arr[0] as $k=>$v) {
				$queryvalues .="`{$k}`={$v},";
			}
			$queryvalues=rtrim($queryvalues, ',');
		}
		return array('sql'=>sprintf($query,$queryvalues),'data'=>$data);//
	}
	public function setTable($tabName){
		$cachefile='./runtime/table_cache/'.$tabName.'.php';
		if(!is_dir(dirname($cachefile))){
			@mkdir(dirname($cachefile),0777,true);
		}
		if(file_exists($cachefile)){
			//add
			if(defined('DEBUG') and DEBUG==TRUE){
				$result=$this->query("desc {$tabName}");
				$fields=array();
				while($row=$this->fetch_assoc($result)){
					$fields[]=strtolower($row['Field']);
				}
				$this->free_result($result);
				return $fields;
			}
			//add
			return include($cachefile);
		}else{
			$result=$this->query("desc {$tabName}");
			$fields=array();
			while($row=$this->fetch_assoc($result)){
				$fields[]=strtolower($row['Field']);
			}
			$this->free_result($result);
			
			$str="<?php\r\n";
			$str.='return ';
			$str.=var_export($fields,true);
			$str.=';';
			$r=file_put_contents($cachefile, $str);
			if(!$r) exit('table_cache failed!');
			return $fields;
		}
	}

	protected function remove_arr($fieldList,$data,$special){
		$arr=array();
		$n=count($special);
		foreach($data as $key=>$value){
			if(in_array($key,$fieldList)){
				if($n>0 && in_array($key,$special)){
					if($value!=''){
						$arr[0][$key]=':'.$key;
						$arr[1][$key]=$value;
					}else{
						continue;
					}
				}else{
					$arr[0][$key]=':'.$key;
					$arr[1][$key]=$value;
				}
			}
		}
		return $arr;
	}
	public function setTableAddon($tabName){
		$cachefile='./runtime/table_cache/'.$tabName.'.addon.php';
		if(!is_dir(dirname($cachefile))){
			@mkdir(dirname($cachefile),0777,true);
		}
		$auto=0;
		if(file_exists($cachefile)){
			//add
			if(defined('DEBUG') and DEBUG==TRUE){
				$result=$this->query("desc {$tabName}");
				$_fields=array();
				$fields=array();
				while($row=$this->fetch_assoc($result)){
					$_fields[]=strtolower($row['Field']);
					if($row['Key']=='PRI'){
						$fields['pk']=strtolower($row['Field']);
					}
					if($row['Extra']=='auto_increment'){
						$auto=1;
					}
				}
				if(!array_key_exists('pk', $fields)){
					$fields['pk']=$_fields[0];		
				}
				$fields['auto']=$auto;
				$this->free_result($result);
				return $fields;
			}
			//add
			return include($cachefile);
		}else{
			$result=$this->query("desc {$tabName}");
			$_fields=array();
			$fields=array();
			while($row=$this->fetch_assoc($result)){
				$_fields[]=strtolower($row['Field']);
				if($row['Key']=='PRI'){
					$fields['pk']=strtolower($row['Field']);
				}
				if($row['Extra']=='auto_increment'){
					$auto=1;
				}
			}
			if(!array_key_exists('pk', $fields)){
				$fields['pk']=$_fields[0];		
			}
			$fields['auto']=$auto;
			$this->free_result($result);
			
			$str="<?php\r\n";
			$str.='return ';
			$str.=var_export($fields,true);
			$str.=';';
			$r=file_put_contents($cachefile, $str);
			if(!$r) exit('table_cache failed!');
			return $fields;
		}
	}

}